package cn.snmp.mutual.uk.stack;

import java.util.*;
import java.io.*;

public class AsnObjectId extends AsnObject implements Comparable {
    private static final String version_id = "@(#)$Id: AsnObjectId.java,v 3.25 2008/08/19 13:34:13 birgita Exp $ Copyright Westhawk Ltd";
    private long[] value;
    static final char[] digits;

    protected AsnObjectId() {
        this.value = new long[0];
        this.type = 6;
    }

    AsnObjectId(final InputStream in, final int len) throws IOException {
        this.value = new long[0];
        final byte[] data = new byte[len];
        if (len != in.read(data, 0, len)) {
            throw new IOException("AsnObjectId(): Not enough data");
        }
        int sids = 1;
        for (int off = 0; off < len; ++off) {
            if (data[off] >= 0) {
                ++sids;
            }
        }
        this.value = new long[sids];
        if (len > 0) {
            this.value[0] = data[0] / 40;
            if (this.value.length > 1) {
                this.value[1] = data[0] % 40;
            }
        }
        int off = 1;
        for (int idx = 2; idx < this.value.length; ++idx) {
            long tval = 0L;
            do {
                tval <<= 7;
                tval |= (data[off] & 0x7F);
            } while (data[off++] < 0);
            this.value[idx] = tval;
        }
    }

    public AsnObjectId(final String s) throws IllegalArgumentException {
        this();
        this.value = this.toArrayOfLongs(s);
    }

    public AsnObjectId(final long[] oida) {
        this();
        final int len = oida.length;
        System.arraycopy(oida, 0, this.value = new long[len], 0, len);
    }

    private long[] toArrayOfLongs(final String s) throws IllegalArgumentException {
        long[] oidArray = new long[0];
        if (s != null && s.length() > 0) {
            final StringTokenizer tok = new StringTokenizer(s, ".");
            final int count = tok.countTokens();
            oidArray = new long[count];
            int n = 0;
            while (tok.hasMoreTokens()) {
                try {
                    final String num = tok.nextToken();
                    final Long val = Long.valueOf(num);
                    oidArray[n] = val;
                    ++n;
                    continue;
                } catch (NumberFormatException exc) {
                    throw new IllegalArgumentException("AsnObjectId(): Bad OID '" + s + "' " + exc.getMessage());
                } catch (NoSuchElementException exc2) {
                    continue;
                }
            }
            return oidArray;
        }
        throw new IllegalArgumentException("AsnObjectId(): Bad OID '" + s + "' ");
    }

    public boolean startsWith(final AsnObjectId prefix) {
        boolean sw = true;
        if (prefix.value.length < this.value.length) {
            for (int pos = 0; pos < prefix.value.length && sw; sw = (prefix.value[pos] == this.value[pos]), ++pos) {
            }
        } else {
            sw = false;
        }
        return sw;
    }

    public void add(final long sub_oid) {
        final int size = this.value.length;
        final long[] tmp_value = this.value;
        System.arraycopy(tmp_value, 0, this.value = new long[size + 1], 0, size);
        this.value[size] = sub_oid;
    }

    public void add(final long[] sub_oid) {
        final int size1 = this.value.length;
        final int size2 = sub_oid.length;
        final long[] tmp_value = this.value;
        System.arraycopy(tmp_value, 0, this.value = new long[size1 + size2], 0, size1);
        System.arraycopy(sub_oid, 0, this.value, size1, size2);
    }

    public void add(final String s) throws IllegalArgumentException {
        final long[] sub_oid = this.toArrayOfLongs(s);
        this.add(sub_oid);
    }

    public long removeLast() {
        long lastSubOid = -1L;
        int size = this.value.length;
        if (size > 0) {
            --size;
            lastSubOid = this.value[size];
            final long[] tmp_value = this.value;
            System.arraycopy(tmp_value, 0, this.value = new long[size], 0, size);
        }
        return lastSubOid;
    }

    @Override
    int size() throws EncodingException {
        int len;
        if (this.value.length > 1) {
            len = this.getSIDLen(this.value[0] * 40L + this.value[1]);
            for (int idx = 2; idx < this.value.length; ++idx) {
                len += this.getSIDLen(this.value[idx]);
            }
        } else if (this.value.length == 1) {
            len = this.getSIDLen(this.value[0] * 40L);
        } else {
            if (this.value.length != 0) {
                throw new EncodingException("Negative numbers cannot be encoded as OID sub-identifiers");
            }
            len = this.getSIDLen(0L);
        }
        return len;
    }

    @Override
    void write(final OutputStream out, final int pos) throws IOException, EncodingException {
        this.AsnBuildHeader(out, (byte) 6, this.size());
        if (AsnObjectId.debug > 10) {
            System.out.println("\tAsnObjectId(): value = " + this.toString() + ", pos = " + pos);
        }
        if (this.value.length > 1) {
            this.EncodeSID(out, this.value[0] * 40L + this.value[1]);
            for (int idx = 2; idx < this.value.length; ++idx) {
                this.EncodeSID(out, this.value[idx]);
            }
        } else if (this.value.length == 1) {
            this.EncodeSID(out, this.value[0] * 40L);
        } else {
            this.EncodeSID(out, 0L);
        }
    }

    private int getSIDLen(long value) {
        int count = 1;
        while ((value >>= 7) != 0L) {
            ++count;
        }
        return count;
    }

    private void EncodeSID(final OutputStream out, final long value) throws IOException {
        byte mask = 15;
        int count = 0;
        mask = 15;
        for (count = 28; count > 0; count -= 7) {
            if ((value >> count & (long) mask) != 0x0L) {
                break;
            }
            mask = 127;
        }
        while (count >= 0) {
            out.write((byte) ((value >> count & (long) mask) | (long) ((count > 0) ? 128 : 0)));
            mask = 127;
            count -= 7;
        }
    }

    public String getValue() {
        return this.toString();
    }

    @Override
    public String toString() {
        return this.toString(this.value);
    }

    public String toString(final long[] v) {
        final StringBuffer buffer = new StringBuffer("");
        if (v.length > 0) {
            for (int n = 0; n < v.length - 1 && n < 100; ++n) {
                if (0L <= v[n] && v[n] <= 9L) {
                    final int i = (int) v[n];
                    buffer.append(AsnObjectId.digits[i]);
                } else {
                    buffer.append(v[n]);
                }
                buffer.append(".");
            }
            if (v.length - 1 > 100) {
                buffer.append("[.. cut ..].");
            }
            buffer.append(v[v.length - 1]);
        }
        return buffer.toString();
    }

    public int getSize() {
        return this.value.length;
    }

    public synchronized long getElementAt(final int index) throws ArrayIndexOutOfBoundsException {
        if (index >= this.value.length) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " + this.value.length);
        }
        try {
            return this.value[index];
        } catch (ArrayIndexOutOfBoundsException exc) {
            throw new ArrayIndexOutOfBoundsException(index + " < 0");
        }
    }

    public long lastElement() throws ArrayIndexOutOfBoundsException {
        return this.getElementAt(this.value.length - 1);
    }

    public long[] getOid() {
        final int len = this.value.length;
        final long[] oida = new long[len];
        System.arraycopy(this.value, 0, oida, 0, len);
        return oida;
    }

    public long[] getSubOid(final int beginIndex, final int endIndex) throws ArrayIndexOutOfBoundsException {
        final int len1 = this.value.length;
        if (beginIndex < 0) {
            throw new ArrayIndexOutOfBoundsException(beginIndex + " < 0");
        }
        if (endIndex > len1) {
            throw new ArrayIndexOutOfBoundsException(endIndex + " > " + len1);
        }
        if (beginIndex > endIndex) {
            throw new ArrayIndexOutOfBoundsException(beginIndex + " > " + endIndex);
        }
        final int len2 = endIndex - beginIndex;
        final long[] oida = new long[len2];
        System.arraycopy(this.value, beginIndex, oida, 0, len2);
        return oida;
    }

    @Override
    public boolean equals(final Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof AsnObjectId) {
            final AsnObjectId anotherOid = (AsnObjectId) anObject;
            int n = this.value.length;
            if (n == anotherOid.value.length) {
                final long[] v1 = this.value;
                final long[] v2 = anotherOid.value;
                int i = 0;
                int j = 0;
                while (n-- != 0) {
                    if (v1[i++] != v2[j++]) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        int h = 0;
        if (h == 0) {
            int off = 0;
            final long[] val = this.value;
            for (int len = this.value.length, i = 0; i < len; ++i) {
                final long l = val[off++];
                final int hi = (int) (l ^ l >>> 32);
                h = 31 * h + hi;
            }
        }
        return h;
    }

    public int compareTo(final Object o) {
        return this.compareTo((AsnObjectId) o);
    }

    public int compareTo(final AsnObjectId b) {
        if (b == null) {
            throw new NullPointerException("Trying to compare with null");
        }
        final int aElts = this.getSize();
        final int bElts = b.getSize();
        if (aElts == 0 && bElts > 0) {
            return -1;
        }
        if (bElts == 0 && aElts > 0) {
            return 1;
        }
        int i = 0;
        while (i < aElts && i < bElts) {
            if (this.getElementAt(i) != b.getElementAt(i)) {
                if (this.getElementAt(i) > b.getElementAt(i)) {
                    return 1;
                }
                return -1;
            } else {
                ++i;
            }
        }
        if (aElts > bElts) {
            return 1;
        }
        if (bElts > aElts) {
            return -1;
        }
        return 0;
    }

    public int leftMostCompare(final int n, final AsnObjectId b) {
        if (b == null) {
            throw new NullPointerException("Trying to compare with null");
        }
        final int aElts = this.getSize();
        final int bElts = b.getSize();
        final int min = Math.min(aElts, bElts);
        if (min >= n) {
            final long[] aOids = this.getSubOid(0, n);
            final long[] bOids = b.getSubOid(0, n);
            final AsnObjectId aCopy = new AsnObjectId(aOids);
            final AsnObjectId bCopy = new AsnObjectId(bOids);
            return aCopy.compareTo(bCopy);
        }
        if (aElts > bElts) {
            return 1;
        }
        if (bElts > aElts) {
            return -1;
        }
        return this.compareTo(b);
    }

    static {
        digits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    }
}
